home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / WINGs / wutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-03  |  13.9 KB  |  587 lines

  1.  
  2.  
  3. /*
  4.  * This event handling stuff was based on Tk.
  5.  * adapted from wevent.c
  6.  */
  7.  
  8. #include "WINGsP.h"
  9.  
  10. #include "../src/config.h"
  11.  
  12. #include <sys/types.h>
  13. #include <unistd.h>
  14.  
  15. #ifdef HAVE_POLL_H
  16. #include <poll.h>
  17. #endif
  18.  
  19.  
  20. #include <X11/Xos.h>
  21.  
  22. #ifdef HAVE_SYS_SELECT_H
  23. # include <sys/select.h>
  24. #endif
  25.  
  26. #include <time.h>
  27.  
  28. #ifndef X_GETTIMEOFDAY
  29. #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*)0)
  30. #endif
  31.  
  32.  
  33.  
  34.  
  35. typedef struct TimerHandler {
  36.     WMCallback        *callback;           /* procedure to call */
  37.     struct timeval    when;               /* when to call the callback */
  38.     void         *clientData;
  39.     struct TimerHandler *next;
  40. } TimerHandler;
  41.  
  42.  
  43. typedef struct IdleHandler {
  44.     WMCallback        *callback;
  45.     void         *clientData;
  46. } IdleHandler;
  47.  
  48.  
  49. typedef struct InputHandler {
  50.     WMInputProc        *callback;
  51.     void        *clientData;
  52.     int            fd;
  53.     int            mask;
  54. } InputHandler;
  55.  
  56.  
  57. /* queue of timer event handlers */
  58. static TimerHandler *timerHandler=NULL;
  59.  
  60. static WMBag *idleHandler=NULL;
  61.  
  62. static WMBag *inputHandler=NULL;
  63.  
  64.  
  65.  
  66. #define timerPending()    (timerHandler)
  67.  
  68.  
  69. static void
  70. rightNow(struct timeval *tv) {
  71.     X_GETTIMEOFDAY(tv);
  72. }
  73.  
  74. /* is t1 after t2 ? */
  75. #define IS_AFTER(t1, t2)    (((t1).tv_sec > (t2).tv_sec) || \
  76.                  (((t1).tv_sec == (t2).tv_sec) \
  77.                   && ((t1).tv_usec > (t2).tv_usec)))
  78.  
  79.  
  80. static void
  81. addmillisecs(struct timeval *tv, int milliseconds)
  82. {
  83.     tv->tv_usec += milliseconds*1000;
  84.  
  85.     tv->tv_sec += tv->tv_usec/1000000;
  86.     tv->tv_usec = tv->tv_usec%1000000;
  87. }
  88.  
  89.  
  90. WMHandlerID
  91. WMAddTimerHandler(int milliseconds, WMCallback *callback, void *cdata)
  92. {
  93.     TimerHandler *handler, *tmp;
  94.  
  95.     handler = malloc(sizeof(TimerHandler));
  96.     if (!handler)
  97.       return NULL;
  98.  
  99.     rightNow(&handler->when);
  100.     addmillisecs(&handler->when, milliseconds);
  101.     handler->callback = callback;
  102.     handler->clientData = cdata;
  103.     /* insert callback in queue, sorted by time left */
  104.     if (!timerHandler || !IS_AFTER(handler->when, timerHandler->when)) {
  105.     /* first in the queue */
  106.     handler->next = timerHandler;
  107.     timerHandler = handler;
  108.     } else {
  109.     tmp = timerHandler;
  110.     while (tmp->next && IS_AFTER(handler->when, tmp->next->when)) {
  111.         tmp = tmp->next;
  112.     }
  113.     handler->next = tmp->next;
  114.     tmp->next = handler;
  115.     }
  116.     return handler;
  117. }
  118.  
  119.  
  120.  
  121. void
  122. WMDeleteTimerWithClientData(void *cdata)
  123. {
  124.     TimerHandler *handler, *tmp;
  125.  
  126.     if (!cdata || !timerHandler)
  127.         return;
  128.  
  129.     tmp = timerHandler;
  130.     if (tmp->clientData==cdata) {
  131.         timerHandler = tmp->next;
  132.         wfree(tmp);
  133.     } else {
  134.         while (tmp->next) {
  135.             if (tmp->next->clientData==cdata) {
  136.                 handler = tmp->next;
  137.                 tmp->next = handler->next;
  138.                 wfree(handler);
  139.                 break;
  140.             }
  141.             tmp = tmp->next;
  142.         }
  143.     }
  144. }
  145.  
  146.  
  147.  
  148. void
  149. WMDeleteTimerHandler(WMHandlerID handlerID)
  150. {
  151.     TimerHandler *tmp, *handler=(TimerHandler*)handlerID;
  152.  
  153.     if (!handler || !timerHandler)
  154.       return;
  155.  
  156.     tmp = timerHandler;
  157.     if (tmp==handler) {
  158.     timerHandler = handler->next;
  159.     wfree(handler);
  160.     } else {
  161.     while (tmp->next) {
  162.         if (tmp->next==handler) {
  163.         tmp->next=handler->next;
  164.         wfree(handler);
  165.         break;
  166.         }
  167.         tmp = tmp->next;
  168.     }
  169.     }
  170. }
  171.  
  172.  
  173.  
  174. WMHandlerID
  175. WMAddIdleHandler(WMCallback *callback, void *cdata)
  176. {
  177.     IdleHandler *handler;
  178.  
  179.     handler = malloc(sizeof(IdleHandler));
  180.     if (!handler)
  181.     return NULL;
  182.  
  183.     handler->callback = callback;
  184.     handler->clientData = cdata;
  185.     /* add handler at end of queue */
  186.     if (!idleHandler) {
  187.     idleHandler = WMCreateBag(16);
  188.     }
  189.     WMPutInBag(idleHandler, handler);
  190.  
  191.     return handler;
  192. }
  193.  
  194.  
  195.  
  196. void
  197. WMDeleteIdleHandler(WMHandlerID handlerID)
  198. {
  199.     IdleHandler *handler = (IdleHandler*)handlerID;
  200.     int pos;
  201.  
  202.     if (!handler || !idleHandler)
  203.     return;
  204.  
  205.     pos = WMGetFirstInBag(idleHandler, handler);
  206.     if (pos >= 0) {
  207.         wfree(handler);
  208.         WMDeleteFromBag(idleHandler, pos);
  209.     }
  210. }
  211.  
  212.  
  213.  
  214. WMHandlerID
  215. WMAddInputHandler(int fd, int condition, WMInputProc *proc, void *clientData)
  216. {
  217.     InputHandler *handler;
  218.  
  219.     handler = wmalloc(sizeof(InputHandler));
  220.  
  221.     handler->fd = fd;
  222.     handler->mask = condition;
  223.     handler->callback = proc;
  224.     handler->clientData = clientData;
  225.  
  226.     if (!inputHandler)
  227.         inputHandler = WMCreateBag(16);
  228.     WMPutInBag(inputHandler, handler);
  229.  
  230.     return handler;
  231. }
  232.  
  233.  
  234.  
  235. void
  236. WMDeleteInputHandler(WMHandlerID handlerID)
  237. {
  238.     InputHandler *handler = (InputHandler*)handlerID;
  239.     int pos;
  240.  
  241.     if (!handler || !inputHandler)
  242.     return;
  243.  
  244.     pos = WMGetFirstInBag(inputHandler, handler);
  245.     if (pos >= 0) {
  246.         wfree(handler);
  247.         WMDeleteFromBag(inputHandler, pos);
  248.     }
  249. }
  250.  
  251.  
  252.  
  253. static Bool
  254. checkIdleHandlers()
  255. {
  256.     IdleHandler *handler;
  257.     WMBag *handlerCopy;
  258.     int i, n;
  259.  
  260.     if (!idleHandler || WMGetBagItemCount(idleHandler)==0) {
  261.     W_FlushIdleNotificationQueue();
  262.         /* make sure an observer in queue didn't added an idle handler */
  263.         return (idleHandler!=NULL && WMGetBagItemCount(idleHandler)>0);
  264.     }
  265.  
  266.     n = WMGetBagItemCount(idleHandler);
  267.     handlerCopy = WMCreateBag(n);
  268.     for (i=0; i<n; i++)
  269.         WMPutInBag(handlerCopy, WMGetFromBag(idleHandler, i));
  270.  
  271.     for (i=0; i<n; i++) {
  272.         handler = WMGetFromBag(handlerCopy, i);
  273.         /* check if the handler still exist or was removed by a callback */
  274.         if (WMGetFirstInBag(idleHandler, handler)<0)
  275.             continue;
  276.  
  277.     (*handler->callback)(handler->clientData);
  278.         WMDeleteIdleHandler(handler);
  279.         wfree(handler);
  280.     }
  281.  
  282.     WMFreeBag(handlerCopy);
  283.  
  284.     W_FlushIdleNotificationQueue();
  285.  
  286.     /* this is not necesarrily False, because one handler can re-add itself */
  287.     return (WMGetBagItemCount(idleHandler)>0);
  288. }
  289.  
  290.  
  291.  
  292. static void
  293. checkTimerHandlers()
  294. {
  295.     TimerHandler *handler;
  296.     struct timeval now;
  297.  
  298.     if (!timerHandler) {
  299.         W_FlushASAPNotificationQueue();
  300.         return;
  301.     }
  302.  
  303.     rightNow(&now);
  304.  
  305.     while (timerHandler && IS_AFTER(now, timerHandler->when)) {
  306.     handler = timerHandler;
  307.     timerHandler = timerHandler->next;
  308.     handler->next = NULL;
  309.     (*handler->callback)(handler->clientData);
  310.     wfree(handler);
  311.     }
  312.  
  313.     W_FlushASAPNotificationQueue();
  314. }
  315.  
  316.  
  317.  
  318. static void
  319. delayUntilNextTimerEvent(struct timeval *delay)
  320. {
  321.     struct timeval now;
  322.  
  323.     if (!timerHandler) {
  324.         /* The return value of this function is only valid if there _are_
  325.          * active timers. */
  326.     delay->tv_sec = 0;
  327.     delay->tv_usec = 0;
  328.     return;
  329.     }
  330.  
  331.     rightNow(&now);
  332.     if (IS_AFTER(now, timerHandler->when)) {
  333.     delay->tv_sec = 0;
  334.     delay->tv_usec = 0;
  335.     } else {
  336.     delay->tv_sec = timerHandler->when.tv_sec - now.tv_sec;
  337.     delay->tv_usec = timerHandler->when.tv_usec - now.tv_usec;
  338.     if (delay->tv_usec < 0) {
  339.         delay->tv_usec += 1000000;
  340.         delay->tv_sec--;
  341.     }
  342.     }
  343. }
  344.  
  345.  
  346. /*
  347.  * This functions will handle input events on all registered file descriptors.
  348.  * Input:
  349.  *    - waitForInput - True if we want the function to wait until an event
  350.  *                     appears on a file descriptor we watch, False if we
  351.  *                     want the function to immediately return if there is
  352.  *                     no data available on the file descriptors we watch.
  353.  * Output:
  354.  *    if waitForInput is False, the function will return False if there are no
  355.  *                     input handlers registered, or if there is no data
  356.  *                     available on the registered ones, and will return True
  357.  *                     if there is at least one input handler that has data
  358.  *                     available.
  359.  *    if waitForInput is True, the function will return False if there are no
  360.  *                     input handlers registered, else it will block until an
  361.  *                     event appears on one of the file descriptors it watches
  362.  *                     and then it will return True.
  363.  *
  364.  * If the retured value is True, the input handlers for the corresponding file
  365.  * descriptors are also called.
  366.  *
  367.  */
  368. static Bool
  369. handleInputEvents(Bool waitForInput)
  370. {
  371. #if defined(HAVE_POLL) && defined(HAVE_POLL_H) && !defined(HAVE_SELECT)
  372.     struct pollfd *fds;
  373.     InputHandler *handler;
  374.     int count, timeout, nfds, i;
  375.  
  376.     if (!inputHandler || (nfds=WMGetBagItemCount(inputHandler))==0) {
  377.         W_FlushASAPNotificationQueue();
  378.         return False;
  379.     }
  380.  
  381.     fds = wmalloc(nfds * sizeof(struct pollfd));
  382.  
  383.     for (i = 0; i<nfds; i++) {
  384.         handler = WMGetFromBag(inputHandler, i);
  385.         fds[i].fd = handler->fd;
  386.         fds[i].events = 0;
  387.     if (handler->mask & WIReadMask)
  388.             fds[i].events |= POLLIN;
  389.  
  390.     if (handler->mask & WIWriteMask)
  391.             fds[i].events |= POLLOUT;
  392.  
  393. #if 0 /* FIXME */
  394.     if (handler->mask & WIExceptMask)
  395.         FD_SET(handler->fd, &eset);
  396. #endif
  397.     }
  398.  
  399.     /*
  400.      * If we don't wait for input, set timeout to return immediately,
  401.      * else setup the timeout to the estimated time until the
  402.      * next timer expires or if no timer is pending to infinite.
  403.      */
  404.     if (!waitForInput) {
  405.         timeout = 0;
  406.     } else if (timerPending()) {
  407.         struct timeval tv;
  408.     delayUntilNextTimerEvent(&tv);
  409.         timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
  410.     } else {
  411.         timeout = -1;
  412.     }
  413.  
  414.     count = poll(fds, nfds, timeout);
  415.  
  416.     if (count > 0) {
  417.         WMBag *handlerCopy = WMCreateBag(nfds);
  418.  
  419.         for (i=0; i<nfds, i++)
  420.             WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
  421.  
  422.         for (i=0; i<nfds; i++) {
  423.         int mask;
  424.  
  425.             handler = WMGetFromBag(handlerCopy, i);
  426.             /* check if the handler still exist or was removed by a callback */
  427.             if (WMGetFirstInBag(inputHandler, handler)<0)
  428.                 continue;
  429.  
  430.         mask = 0;
  431.  
  432.             if ((handler->mask & WIReadMask) &&
  433.                 (fds[i].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)))
  434.                 mask |= WIReadMask;
  435.  
  436.             if ((handler->mask & WIWriteMask) &&
  437.                 (fds[i].revents & (POLLOUT | POLLWRBAND)))
  438.                 mask |= WIWriteMask;
  439.  
  440.             if ((handler->mask & WIExceptMask) &&
  441.                 (fds[i].revents & (POLLHUP | POLLNVAL | POLLERR)))
  442.                 mask |= WIExceptMask;
  443.  
  444.         if (mask!=0 && handler->callback) {
  445.         (*handler->callback)(handler->fd, mask,
  446.                      handler->clientData);
  447.         }
  448.         }
  449.  
  450.         WMFreeBag(handlerCopy);
  451.     }
  452.  
  453.     wfree(fds);
  454.  
  455.     W_FlushASAPNotificationQueue();
  456.  
  457.     return (count > 0);
  458. #else /* not HAVE_POLL */
  459. #ifdef HAVE_SELECT
  460.     struct timeval timeout;
  461.     struct timeval *timeoutPtr;
  462.     fd_set rset, wset, eset;
  463.     int maxfd, nfds, i;
  464.     int count;
  465.     InputHandler *handler;
  466.  
  467.     if (!inputHandler || (nfds=WMGetBagItemCount(inputHandler))==0) {
  468.         W_FlushASAPNotificationQueue();
  469.         return False;
  470.     }
  471.  
  472.     FD_ZERO(&rset);
  473.     FD_ZERO(&wset);
  474.     FD_ZERO(&eset);
  475.  
  476.     maxfd = 0;
  477.  
  478.     for (i=0; i<nfds; i++) {
  479.         handler = WMGetFromBag(inputHandler, i);
  480.     if (handler->mask & WIReadMask)
  481.         FD_SET(handler->fd, &rset);
  482.  
  483.     if (handler->mask & WIWriteMask)
  484.         FD_SET(handler->fd, &wset);
  485.  
  486.     if (handler->mask & WIExceptMask)
  487.         FD_SET(handler->fd, &eset);
  488.  
  489.     if (maxfd < handler->fd)
  490.         maxfd = handler->fd;
  491.     }
  492.  
  493.     /*
  494.      * If we don't wait for input, set timeout to return immediately,
  495.      * else setup the timeout to the estimated time until the
  496.      * next timer expires or if no timer is pending to infinite.
  497.      */
  498.     if (!waitForInput) {
  499.         timeout.tv_sec = 0;
  500.         timeout.tv_usec = 0;
  501.         timeoutPtr = &timeout;
  502.     } else if (timerPending()) {
  503.     delayUntilNextTimerEvent(&timeout);
  504.     timeoutPtr = &timeout;
  505.     } else {
  506.     timeoutPtr = (struct timeval*)0;
  507.     }
  508.  
  509.     count = select(1 + maxfd, &rset, &wset, &eset, timeoutPtr);
  510.  
  511.     if (count > 0) {
  512.         WMBag *handlerCopy = WMCreateBag(nfds);
  513.  
  514.         for (i=0; i<nfds; i++)
  515.             WMPutInBag(handlerCopy, WMGetFromBag(inputHandler, i));
  516.  
  517.         for (i=0; i<nfds; i++) {
  518.         int mask;
  519.  
  520.             handler = WMGetFromBag(handlerCopy, i);
  521.             /* check if the handler still exist or was removed by a callback */
  522.             if (WMGetFirstInBag(inputHandler, handler)<0)
  523.                 continue;
  524.  
  525.         mask = 0;
  526.  
  527.         if ((handler->mask & WIReadMask) && FD_ISSET(handler->fd, &rset))
  528.         mask |= WIReadMask;
  529.  
  530.         if ((handler->mask & WIWriteMask) && FD_ISSET(handler->fd, &wset))
  531.         mask |= WIWriteMask;
  532.  
  533.         if ((handler->mask & WIExceptMask) && FD_ISSET(handler->fd, &eset))
  534.         mask |= WIExceptMask;
  535.  
  536.         if (mask!=0 && handler->callback) {
  537.         (*handler->callback)(handler->fd, mask,
  538.                      handler->clientData);
  539.         }
  540.         }
  541.  
  542.         WMFreeBag(handlerCopy);
  543.     }
  544.  
  545.     W_FlushASAPNotificationQueue();
  546.  
  547.     return (count > 0);
  548. #else /* not HAVE_SELECT, not HAVE_POLL */
  549. Neither select nor poll. You lose.
  550. #endif /* HAVE_SELECT */
  551. #endif /* HAVE_POLL */
  552. }
  553.  
  554.  
  555. void
  556. WHandleEvents()
  557. {
  558.     /* Check any expired timers */
  559.     checkTimerHandlers();
  560.  
  561.     /* We need to make sure that we have some input handler before calling
  562.      * checkIdleHandlers() in a while loop, because else the while loop
  563.      * can run forever (if some idle handler reinitiates itself).
  564.      */
  565.     if (inputHandler && WMGetBagItemCount(inputHandler)>0) {
  566.         /* Do idle and timer stuff while there are no input events */
  567.         /* Check again if there are still input handlers, because some idle
  568.          * handler could have removed them */
  569.         while (checkIdleHandlers() && WMGetBagItemCount(inputHandler)>0 &&
  570.                !handleInputEvents(False)) {
  571.             /* dispatch timer events */
  572.             checkTimerHandlers();
  573.         }
  574.     } else {
  575.         checkIdleHandlers();
  576.         /* dispatch timer events */
  577.         checkTimerHandlers();
  578.     }
  579.  
  580.     handleInputEvents(True);
  581.  
  582.     /* Check any expired timers */
  583.     checkTimerHandlers();
  584. }
  585.  
  586.  
  587.